/*
Copyright (C) 2022-2024 Inspur Corp.

SPDX-License-Identifier: Apache-2.0
*/
package controller

import (
	"encoding/json"
	"github.com/asaskevich/govalidator"
	"github.com/emicklei/go-restful"
	"icep/auth"
	"icep/bean/param"
	"icep/common/constant"
	"icep/common/cryptoprovider"
	"icep/common/file"
	"icep/common/http"
	"icep/common/utils"
	"icep/common/uuid"
	"mime/multipart"
	"os"
	"path/filepath"
	"strconv"
	"time"
	"unicode/utf8"
)

func (ic *IcepController) GetFileEvidencePage(req *restful.Request, resp *restful.Response) {
	userId := auth.GetCurrentUserId(req)

	strPageSize := req.QueryParameter("pageSize")
	strPageNo := req.QueryParameter("pageNum")
	evidenceName := req.QueryParameter("evidenceName")
	evidenceType := req.QueryParameter("evidenceType")
	useEncrypt := req.QueryParameter("useEncrypt")
	startTime := req.QueryParameter("startTime")
	endTime := req.QueryParameter("endTime")

	if evidenceName != "" && utf8.RuneCountInString(evidenceName) > 50 {
		http.Failed(resp, "存证名称50字符以内")
		return
	}
	if evidenceType != "" {
		evidenceTypes := []string{"0", "1", "2", "3", "4"} //存证类型，1：图片，2：文档，3：音频视频，4：其他
		if !IsContain(evidenceTypes, evidenceType) {
			http.Failed(resp, "格式不符")
			return
		}
	}
	//查询时间符合判断
	var stime time.Time
	var err error
	if startTime != "" {
		stime, err = time.Parse(constant.TimeLayout, startTime)
		if err != nil {
			http.Failed(resp, "时间格式不符")
			return
		}
	}
	if endTime != "" {
		etime, err := time.Parse(constant.TimeLayout, endTime)
		if err != nil {
			http.Failed(resp, "时间格式不符")
			return
		} else if startTime != "" {
			if stime.After(etime) {
				http.Failed(resp, "开始时间大于结束时间")
				return
			}
		}
	}

	if useEncrypt != "" {
		if !(useEncrypt == "0" || useEncrypt == "1") {
			http.Failed(resp, "格式不符")
			return
		}
	}

	pageSize := 10
	if strPageSize != "" {
		pageSize, err = strconv.Atoi(strPageSize)
		if err != nil {
			http.Failed(resp, "页大小格式不符")
			return
		}
	}
	pageNo := 1
	if strPageNo != "" {
		pageNo, err = strconv.Atoi(strPageNo)
		if err != nil {
			http.Failed(resp, "页号格式不符")
			return
		}
	}

	page, err := ic.IcepService.GetFileEvidencePage(userId, evidenceName, evidenceType, useEncrypt, startTime, endTime, pageSize, pageNo)
	if err != nil {
		http.Failed(resp, "查询失败")
		return
	}
	http.OkWithData(resp, "查询成功", page)
}

func (ic *IcepController) GetFileEvidenceList(req *restful.Request, resp *restful.Response) {}

func (ic *IcepController) GetFileEvidenceInfo(req *restful.Request, resp *restful.Response) {
	evidenceId := req.QueryParameter("evidenceId")
	if evidenceId == "" {
		http.Failed(resp, "存证编号为空")
		return
	}
	userId := auth.GetCurrentUserId(req)

	fileEvidenceInfo, err := ic.IcepService.GetFileEvidenceInfo(userId, auth.GetCurrentTenantId(req), evidenceId)
	if err != nil {
		http.Failed(resp, "查询失败")
		return
	}
	http.OkWithData(resp, "查询成功", fileEvidenceInfo)
}

func (ic *IcepController) CreateFileEvidence(req *restful.Request, resp *restful.Response) {
	files, err := http.ReadFiles(req, "file")
	if err != nil {
		http.Failed(resp, "存证文件获取失败")
		return
	}

	var fileEvidenceParam param.FileEvidenceParam
	fileEvidenceParam.EvidenceName, _ = req.BodyParameter("evidenceName")
	fileEvidenceParam.EvidenceType, _ = req.BodyParameter("evidenceType")
	fileEvidenceParam.UseEncrypt, _ = req.BodyParameter("useEncrypt")

	if fileEvidenceParam.EvidenceType != "" {
		evidenceTypes := []string{"1", "2", "3", "4"} //存证类型，1：图片，2：文档，3：音频视频，4：其他
		if !IsContain(evidenceTypes, fileEvidenceParam.EvidenceType) {
			http.Failed(resp, "格式不符")
			return
		}
	} else {
		http.Failed(resp, "存证类型为空")
		return
	}
	checkResult, msg := CheckFiles(files, fileEvidenceParam.EvidenceType)
	if !checkResult {
		http.Failed(resp, msg)
		return
	}

	userId := auth.GetCurrentUserId(req)
	if fileEvidenceParam.EvidenceName == "" || utf8.RuneCountInString(fileEvidenceParam.EvidenceName) > 50 {
		http.Failed(resp, "存证名称为空或字符超过50")
		return
	}
	if fileEvidenceParam.UseEncrypt != "" {
		if !(fileEvidenceParam.UseEncrypt == "0" || fileEvidenceParam.UseEncrypt == "1") {
			http.Failed(resp, "格式不符")
			return
		}
	} else {
		http.Failed(resp, "是否加密存储为空")
		return
	}

	err = ic.IcepService.CreateFileEvidence(userId, fileEvidenceParam, files)
	if err != nil {
		http.Failed(resp, "存证失败")
		return
	}
	http.Ok(resp, "存证成功")
}

func (ic *IcepController) CreateFileNokeyEvidence(req *restful.Request, resp *restful.Response) {
	files, fileErr := http.ReadFiles(req, "file")
	var fileEvidenceParam param.FileEvidenceParam
	fileEvidenceParam.EvidenceName, _ = req.BodyParameter("evidenceName")
	fileEvidenceParam.EvidenceType, _ = req.BodyParameter("evidenceType")
	fileEvidenceParam.UseEncrypt, _ = req.BodyParameter("useEncrypt")
	fileEvidenceParam.Signature, _ = req.BodyParameter("signature")
	fileEvidenceParam.EncryptedSecret, _ = req.BodyParameter("encryptedSecret")
	userId := auth.GetCurrentUserId(req)
	if fileEvidenceParam.EvidenceName == "" || utf8.RuneCountInString(fileEvidenceParam.EvidenceName) > 50 {
		http.Failed(resp, "存证名称为空或字符超过50")
		return
	}
	if fileEvidenceParam.EvidenceType != "" {
		evidenceTypes := []string{"0", "1", "2", "3", "4"} //存证类型，0：哈希，1：图片，2：文档，3：音频视频，4：其他
		if !IsContain(evidenceTypes, fileEvidenceParam.EvidenceType) {
			http.Failed(resp, "格式不符")
			return
		}
	} else {
		http.Failed(resp, "存证类型为空")
		return
	}
	if fileEvidenceParam.UseEncrypt != "" {
		if !(fileEvidenceParam.UseEncrypt == "0" || fileEvidenceParam.UseEncrypt == "1") {
			http.Failed(resp, "格式不符")
			return
		} else if fileEvidenceParam.EvidenceType != "0" && fileEvidenceParam.UseEncrypt == "1" && fileEvidenceParam.EncryptedSecret == "" {
			http.Failed(resp, "加密存储下对称秘钥为空")
			return
		}
	} else {
		http.Failed(resp, "是否加密存储为空")
		return
	}

	if fileEvidenceParam.Signature == "" {
		http.Failed(resp, "签名为空")
		return
	}

	if fileEvidenceParam.EvidenceType == "0" { //hash 类文件处理
		fileInfos, _ := req.BodyParameter("fileInfo")
		if fileInfos != "" {
			jsonBool := json.Valid([]byte(fileInfos))
			if !jsonBool {
				http.Failed(resp, "fileInfos数据格式不符")
				return
			}
		} else {
			http.Failed(resp, "fileInfos数据为空")
			return
		}
		if err := json.Unmarshal([]byte(fileInfos), &fileEvidenceParam.FileInfo); err != nil {
			http.Failed(resp, "fileInfo数据格式不符")
			return
		}
		rest, msg := CheckFileInfos(fileEvidenceParam.FileInfo)
		if !rest {
			http.Failed(resp, msg)
			return
		}
	} else { //文件存证
		if fileErr != nil {
			http.Failed(resp, "存证文件获取失败")
			return
		}
		checkResult, msg := CheckFiles(files, fileEvidenceParam.EvidenceType)
		if !checkResult {
			http.Failed(resp, msg)
			return
		}
	}

	err := ic.IcepService.CreateFileNokeyEvidence(userId, fileEvidenceParam, files)
	if err != nil {
		http.Failed(resp, "false")
		return
	}
	http.Ok(resp, "success")
}

func (ic *IcepController) CreateFileHashEvidence(req *restful.Request, resp *restful.Response) {
	userId := auth.GetCurrentUserId(req)
	var fileEvidenceParam param.FileEvidenceParam
	if err := req.ReadEntity(&fileEvidenceParam); err != nil {
		http.Failed(resp, err)
		return
	}
	if fileEvidenceParam.EvidenceName == "" || utf8.RuneCountInString(fileEvidenceParam.EvidenceName) > 50 {
		http.Failed(resp, "存证名称为空或字符超过50")
		return
	}
	//存证名称不可重复
	existDatas, _ := ic.IcepService.GetFileEvidenceList(userId, fileEvidenceParam)
	if existDatas != nil && len(existDatas) > 0 {
		// EvidenceName already exists
		http.Failed(resp, "存证名称已存在")
		return
	}
	rest, msg := CheckFileInfos(fileEvidenceParam.FileInfo)
	if !rest {
		http.Failed(resp, msg)
		return
	}

	err := ic.IcepService.CreateFileHashEvidence(userId, fileEvidenceParam)
	if err != nil {
		http.Failed(resp, "false")
		return
	}
	http.Ok(resp, "success")
}

func (ic *IcepController) DecryptFileEvidence(req *restful.Request, resp *restful.Response) {
	userId := auth.GetCurrentUserId(req)
	var filevidenceParam param.FileEvidenceParam
	if err := req.ReadEntity(&filevidenceParam); err != nil {
		http.Failed(resp, err)
		return
	}
	var userPrivateKey string
	if filevidenceParam.Password == "" {
		http.Failed(resp, "解密参数不符")
		return
	}
	user, result := ic.IcepService.GetUserByUserId(userId)
	if !result || !cryptoprovider.Get().Check(user.Password, filevidenceParam.Password) {
		http.Failed(resp, "该用户权限不足")
		return
	}
	userPrivateKey = user.PrvKey
	if userPrivateKey == "" {
		http.Failed(resp, "非秘钥托管类加密不支持")
		return
	}
	if filevidenceParam.EvidenceId == "" {
		http.Failed(resp, "存证编号为空")
		return
	}
	if filevidenceParam.FileUrl == "" {
		http.Failed(resp, "文件url为空")
		return
	}

	decryptedFilePath, decryptedFileName, result := ic.IcepService.DecryptFileEvidence(userPrivateKey, filevidenceParam.EvidenceId, filevidenceParam.FileUrl)
	if !result {
		http.Failed(resp, "解密失败")
		return
	}
	http.OkWithFile(req, resp, decryptedFileName, decryptedFilePath)
	errs := file.DeleteFile(decryptedFilePath)
	if errs != nil {
		log.Errorf("删除本地解密文件失败: %s", errs)
	}
}

func IsContain(items []string, item string) bool {
	for _, eachItem := range items {
		if eachItem == item {
			return true
		}
	}
	return false
}

func CheckFiles(files []*multipart.FileHeader, filetype string) (result bool, msg string) {
	if len(files) > 10 || len(files) < 1 {
		return false, "存证文件不存在或数量超过10个"
	}
	for _, fileHeader := range files {
		result = CheckFileMimeType(fileHeader)
		if !result {
			return false, "文件扩展名不匹配！"
		}
		filename := fileHeader.Filename
		if len(filename) > 64 {
			return false, "文件名称过长，超过64个字符"
		}
		if !CheckFileType(filename, filetype) {
			return false, "文件类型错误！"
		}
		size := fileHeader.Size
		//存证类型，0：哈希，1：图片（100M），2：文档（20M），3：音频视频(500M)，4：其他(200M)
		switch filetype {
		case "1":
			if size > 104857600 {
				return false, filename + "图片大小超过100M"
			}
		case "2":
			if size > 20971520 {
				return false, filename + "文档大小超过20M"
			}
		case "3":
			if size > 524288000 {
				return false, filename + "音视频大小超过500M"
			}
		case "4":
			if size > 209715200 {
				return false, filename + "其他大小超过200M"
			}
		}

	}
	return true, ""
}

var (
	ImgExtArr = []string{".png", ".jpg", ".zip"}
	DocExtArr = []string{".doc", ".docx", ".pdf", ".ppt", ".html", ".txt", ".xlsx", ".zip"}
	AVExtArr  = []string{".mp3", ".mp4", ".zip"}
	AllExtArr = []string{".png", ".jpg", ".doc", ".docx", ".pdf", ".ppt", ".html", ".txt", ".xlsx", ".mp3", ".mp4", ".zip"}
)

func CheckFileType(fileName, fileType string) bool {
	ext := filepath.Ext(fileName)
	switch fileType {
	case "1":
		return IsContain(ImgExtArr, ext)
	case "2":
		return IsContain(DocExtArr, ext)
	case "3":
		return IsContain(AVExtArr, ext)
	case "4":
		return IsContain(AllExtArr, ext)
	}
	return false
}

func CheckFileInfos(files []param.FileInfo) (result bool, msg string) {
	if len(files) > 10 || len(files) < 1 {
		return false, "哈希类文件信息不存在或数量超过10个"
	}
	for _, fileInfo := range files {
		if fileInfo.Name == "" || utf8.RuneCountInString(fileInfo.Name) > 50 {
			return false, fileInfo.Name + "文件名称不存在或超过50字符"
		}
		if fileInfo.Url == "" || !govalidator.IsURL(fileInfo.Url) {
			return false, fileInfo.Name + "文件url不存在或格式有误"
		}
		if fileInfo.Hash == "" {
			return false, fileInfo.Name + "文件hash不存在"
		}

	}
	return true, ""
}

func CheckFileMimeType(fileHeader *multipart.FileHeader) bool {
	tempDir := os.TempDir()
	//tempDir := "D:\\ichain"

	path := utils.StrJoin(tempDir, constant.FileSeparator, uuid.Generate(), constant.FileSeparator)
	fileName := fileHeader.Filename
	filePath := utils.StrJoin(path, fileName)
	file.CreateIfNotExists(filePath, false)
	err := file.SaveUploadedFile(fileHeader, filePath)
	if err != nil {
		log.Errorf("save uploaded file  %s error: %v", fileName, err)
		return false
	}
	defer func() {
		err := file.DeleteFileAll(path)
		if err != nil {
			log.Errorf("删除本地临时文件[%s]失败: %v", path, err)
		}
	}()

	return file.CheckFileMimeType(filePath)
}
